package main

import (
	"context"
	"example_shorten/dao"
	url "example_shorten/kitex_gen/example/shorten/url"
	"example_shorten/model"
	"fmt"
	"log"
)

// UrlServiceImpl 实现了 IDL 中定义的最后一个服务接口。
type UrlServiceImpl struct{}

// urlMap 用于存储短链接和长链接的映射关系
var (
	urlMap = make(map[string]string)
)

// GetUrl 实现了 UrlServiceImpl 接口。
// 它根据请求的短链接获取对应的长链接信息。
func (s *UrlServiceImpl) GetUrl(ctx context.Context, req *url.GetUrlReq) (resp *url.GetUrlResp, err error) {
	// 方式一：从进程内存中获取短链接对应的长链接
	return getUrl01(req)
	// 方式二：从数据库中获取短链接对应的长链接
	// return getUrl02(req)
	// 方式三：从缓存中获取短链接对应的长链接
	// return getUrl03(ctx, req)
}

// GenerateUrl 实现了 UrlServiceImpl 接口。
// 它根据请求的长链接生成对应的短链接。
func (s *UrlServiceImpl) GenerateUrl(ctx context.Context, req *url.GenerateUrlReq) (resp *url.GenerateUrlResp, err error) {
	// 方式一：存储于进程内存中
	return generateUrl01(req)
	// 方式二：存储于数据库中
	// return generateUrl02(req)
}

// getUrl01 从进程内存中的 urlMap 获取短链接对应的长链接信息。
func getUrl01(req *url.GetUrlReq) (resp *url.GetUrlResp, err error) {
	resp = url.NewGetUrlResp()
	// 第一步： 从 urlMap 中获取短链接对应的长链接
	longURL, ok := urlMap[req.Url]
	// 打印日志
	log.Printf("longURL: %s, ok: %v", longURL, ok)
	// 如果短链接不存在，返回 404 Not Found 错误
	if !ok {
		log.Fatalf("long URL not found: " + req.Url)
		return
	}
	resp.Url = &url.Url{
		LongUrl:  longURL,
		ShortUrl: req.Url,
	}
	return
}

// getUrl02 从数据库中获取短链接对应的长链接信息。
func getUrl02(req *url.GetUrlReq) (resp *url.GetUrlResp, err error) {
	resp = url.NewGetUrlResp()
	// 第二步：从数据库中获取短连接对应的长连接
	db_url, err2 := dao.QueryUrlByShortUrl(req.Url)
	if err2 != nil {
		log.Fatal(err2)
		return nil, err2
	}
	// 将 model.Url 转换为 url.Url
	resp.Url = &url.Url{
		LongUrl:  db_url.LongUrl,
		ShortUrl: db_url.ShortUrl,
		Hits:     int64(db_url.Hits),
	}
	if err != nil {
		log.Fatalf("Failed to set key: %v", err)
	}
	return
}

// getUrl03 首先尝试从缓存中获取短链接对应的长链接信息，如果未找到，则从数据库中获取。
func getUrl03(ctx context.Context, req *url.GetUrlReq) (resp *url.GetUrlResp, err error) {
	resp = url.NewGetUrlResp()
	// 第三步: 从缓存中获取短连接对应的长连接
	longUrl, err := dao.CacheSession.Get(ctx, req.Url).Result()
	if longUrl != "" {
		resp.Url = &url.Url{
			LongUrl:  longUrl,
			ShortUrl: req.Url,
		}
		return

	}
	// 第二步：从数据库中获取短连接对应的长连接
	db_url, err2 := dao.QueryUrlByShortUrl(req.Url)
	if err2 != nil {
		log.Fatal(err2)
		return nil, err2
	}
	// 将 model.Url 转换为 url.Url
	resp.Url = &url.Url{
		LongUrl:  db_url.LongUrl,
		ShortUrl: db_url.ShortUrl,
		Hits:     int64(db_url.Hits),
	}
	err = dao.CacheSession.Set(ctx, db_url.ShortUrl, db_url.LongUrl, 0).Err()
	if err != nil {
		log.Fatalf("Failed to set key: %v", err)
	}
	return
}

// generateUrl01 在进程内存中生成并存储短链接与长链接的映射关系。
func generateUrl01(req *url.GenerateUrlReq) (resp *url.GenerateUrlResp, err error) {
	resp = url.NewGenerateUrlResp()
	resp.ShortUrl = generateShortURL(req.Url)
	// 存储短链接和长链接的映射关系
	urlMap[resp.ShortUrl] = req.Url
	return
}

// generateUrl02 在数据库中生成并存储短链接与长链接的映射关系。
func generateUrl02(req *url.GenerateUrlReq) (resp *url.GenerateUrlResp, err error) {
	resp = url.NewGenerateUrlResp()
	resp.ShortUrl = generateShortURL(req.Url)
	// 查询是否已存在该长链接
	db_url, err := dao.QueryUrlByLongUrl(req.Url)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	// 如果已存在，直接返回短链接
	if db_url.LongUrl != "" {
		resp.ShortUrl = db_url.ShortUrl
		return
	}
	// 入库
	err = dao.CreateUrl(&model.Url{
		LongUrl:  req.Url,
		ShortUrl: resp.ShortUrl,
		Hits:     0,
	})
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	return
}

// generateShortURL 使用简单的哈希算法生成短链接。
func generateShortURL(longURL string) string {
	// 这里可以使用任何你喜欢的算法来生成短链接
	// 为了简单起见，我们只是将长链接的哈希值作为短链接
	return fmt.Sprintf("%x", hash(longURL))
}

// hash 是一个简单的哈希算法，用于生成短链接。
func hash(s string) uint32 {
	// 简单的哈希算法示例
	h := uint32(0)
	for _, c := range s {
		// 计算哈希值
		h = h*31 + uint32(c)
	}
	return h
}
